﻿// This file is part of Better Enums, released under the BSD 2-clause license.
// See LICENSE.md for details, or visit http://github.com/aantron/better-enums.

#pragma once

#ifndef BETTER_ENUMS_ENUM_H
    #define BETTER_ENUMS_ENUM_H

    #include <cstddef>
    #include <cstring>
    #include <iosfwd>
    #include <stdexcept>

    // in-line, non-#pragma warning handling
    // not supported in very old compilers (namely gcc 4.4 or less)
    #ifdef __GNUC__
        #ifdef __clang__
            #define BETTER_ENUMS_IGNORE_OLD_CAST_HEADER _Pragma("clang diagnostic push")
            #define BETTER_ENUMS_IGNORE_OLD_CAST_BEGIN  _Pragma("clang diagnostic ignored \"-Wold-style-cast\"")
            #define BETTER_ENUMS_IGNORE_OLD_CAST_END    _Pragma("clang diagnostic pop")
            #define BETTER_ENUMS_IGNORE_ATTRIBUTES_HEADER
            #define BETTER_ENUMS_IGNORE_ATTRIBUTES_BEGIN
            #define BETTER_ENUMS_IGNORE_ATTRIBUTES_END
        #else
            #define BETTER_ENUMS_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100)
            #if BETTER_ENUMS_GCC_VERSION > 40400
                #define BETTER_ENUMS_IGNORE_OLD_CAST_HEADER _Pragma("GCC diagnostic push")
                #define BETTER_ENUMS_IGNORE_OLD_CAST_BEGIN  _Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
                #define BETTER_ENUMS_IGNORE_OLD_CAST_END    _Pragma("GCC diagnostic pop")
                #if (BETTER_ENUMS_GCC_VERSION >= 70300)
                    #define BETTER_ENUMS_IGNORE_ATTRIBUTES_HEADER _Pragma("GCC diagnostic push")
                    #define BETTER_ENUMS_IGNORE_ATTRIBUTES_BEGIN  _Pragma("GCC diagnostic ignored \"-Wattributes\"")
                    #define BETTER_ENUMS_IGNORE_ATTRIBUTES_END    _Pragma("GCC diagnostic pop")
                #else
                    #define BETTER_ENUMS_IGNORE_ATTRIBUTES_HEADER
                    #define BETTER_ENUMS_IGNORE_ATTRIBUTES_BEGIN
                    #define BETTER_ENUMS_IGNORE_ATTRIBUTES_END
                #endif
            #else
                #define BETTER_ENUMS_IGNORE_OLD_CAST_HEADER
                #define BETTER_ENUMS_IGNORE_OLD_CAST_BEGIN
                #define BETTER_ENUMS_IGNORE_OLD_CAST_END
                #define BETTER_ENUMS_IGNORE_ATTRIBUTES_HEADER
                #define BETTER_ENUMS_IGNORE_ATTRIBUTES_BEGIN
                #define BETTER_ENUMS_IGNORE_ATTRIBUTES_END
            #endif
        #endif
    #else  // empty definitions for compilers that don't support _Pragma
        #define BETTER_ENUMS_IGNORE_OLD_CAST_HEADER
        #define BETTER_ENUMS_IGNORE_OLD_CAST_BEGIN
        #define BETTER_ENUMS_IGNORE_OLD_CAST_END
        #define BETTER_ENUMS_IGNORE_ATTRIBUTES_HEADER
        #define BETTER_ENUMS_IGNORE_ATTRIBUTES_BEGIN
        #define BETTER_ENUMS_IGNORE_ATTRIBUTES_END
    #endif

// Feature detection.

    #ifdef __GNUC__
        #ifdef __clang__
            #if __has_feature(cxx_constexpr)
                #define BETTER_ENUMS_HAVE_CONSTEXPR
            #endif
            #if !defined(__EXCEPTIONS) || !__has_feature(cxx_exceptions)
                #define BETTER_ENUMS_NO_EXCEPTIONS
            #endif
        #else
            #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
                #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6))
                    #define BETTER_ENUMS_HAVE_CONSTEXPR
                #endif
            #endif
            #ifndef __EXCEPTIONS
                #define BETTER_ENUMS_NO_EXCEPTIONS
            #endif
        #endif
    #endif

    #ifdef _MSC_VER
        #if _MSC_VER >= 1911
            #define BETTER_ENUMS_HAVE_CONSTEXPR
        #endif
        #ifdef __clang__
            #if __has_feature(cxx_constexpr)
                #define BETTER_ENUMS_HAVE_CONSTEXPR
            #endif
        #endif
        #ifndef _CPPUNWIND
            #define BETTER_ENUMS_NO_EXCEPTIONS
        #endif
        #if _MSC_VER < 1600
            #define BETTER_ENUMS_VC2008_WORKAROUNDS
        #endif
    #endif

    #ifdef BETTER_ENUMS_CONSTEXPR
        #define BETTER_ENUMS_HAVE_CONSTEXPR
    #endif

    #ifdef BETTER_ENUMS_NO_CONSTEXPR
        #ifdef BETTER_ENUMS_HAVE_CONSTEXPR
            #undef BETTER_ENUMS_HAVE_CONSTEXPR
        #endif
    #endif

    // GCC (and maybe clang) can be made to warn about using 0 or NULL when nullptr
    // is available, so Better Enums tries to use nullptr. This passage uses
    // availability of constexpr as a proxy for availability of nullptr, i.e. it
    // assumes that nullptr is available when compiling on the right versions of gcc
    // and clang with the right -std flag. This is actually slightly wrong, because
    // nullptr is also available in Visual C++, but constexpr isn't. This
    // imprecision doesn't matter, however, because VC++ doesn't have the warnings
    // that make using nullptr necessary.
    #ifdef BETTER_ENUMS_HAVE_CONSTEXPR
        #define BETTER_ENUMS_CONSTEXPR_ constexpr
        #define BETTER_ENUMS_NULLPTR    nullptr
    #else
        #define BETTER_ENUMS_CONSTEXPR_
        #define BETTER_ENUMS_NULLPTR NULL
    #endif

    #ifndef BETTER_ENUMS_NO_EXCEPTIONS
        #define BETTER_ENUMS_IF_EXCEPTIONS(x) x
    #else
        #define BETTER_ENUMS_IF_EXCEPTIONS(x)
    #endif

    #ifdef __GNUC__
        #define BETTER_ENUMS_UNUSED __attribute__((__unused__))
    #else
        #define BETTER_ENUMS_UNUSED
    #endif

// Higher-order preprocessor macros.

    #ifdef BETTER_ENUMS_MACRO_FILE
        #include BETTER_ENUMS_MACRO_FILE
    #else

        #define BETTER_ENUMS_PP_MAP(macro, data, ...)                                                                  \
            BETTER_ENUMS_ID(BETTER_ENUMS_APPLY(BETTER_ENUMS_PP_MAP_VAR_COUNT,                                          \
                                               BETTER_ENUMS_PP_COUNT(__VA_ARGS__))(macro, data, __VA_ARGS__))

        #define BETTER_ENUMS_PP_MAP_VAR_COUNT(count) BETTER_ENUMS_M##count

        #define BETTER_ENUMS_APPLY(macro, ...) BETTER_ENUMS_ID(macro(__VA_ARGS__))

        #define BETTER_ENUMS_ID(x) x

        #define BETTER_ENUMS_M1(m, d, x)       m(d, 0, x)
        #define BETTER_ENUMS_M2(m, d, x, ...)  m(d, 1, x) BETTER_ENUMS_ID(BETTER_ENUMS_M1(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M3(m, d, x, ...)  m(d, 2, x) BETTER_ENUMS_ID(BETTER_ENUMS_M2(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M4(m, d, x, ...)  m(d, 3, x) BETTER_ENUMS_ID(BETTER_ENUMS_M3(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M5(m, d, x, ...)  m(d, 4, x) BETTER_ENUMS_ID(BETTER_ENUMS_M4(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M6(m, d, x, ...)  m(d, 5, x) BETTER_ENUMS_ID(BETTER_ENUMS_M5(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M7(m, d, x, ...)  m(d, 6, x) BETTER_ENUMS_ID(BETTER_ENUMS_M6(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M8(m, d, x, ...)  m(d, 7, x) BETTER_ENUMS_ID(BETTER_ENUMS_M7(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M9(m, d, x, ...)  m(d, 8, x) BETTER_ENUMS_ID(BETTER_ENUMS_M8(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M10(m, d, x, ...) m(d, 9, x) BETTER_ENUMS_ID(BETTER_ENUMS_M9(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M11(m, d, x, ...) m(d, 10, x) BETTER_ENUMS_ID(BETTER_ENUMS_M10(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M12(m, d, x, ...) m(d, 11, x) BETTER_ENUMS_ID(BETTER_ENUMS_M11(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M13(m, d, x, ...) m(d, 12, x) BETTER_ENUMS_ID(BETTER_ENUMS_M12(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M14(m, d, x, ...) m(d, 13, x) BETTER_ENUMS_ID(BETTER_ENUMS_M13(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M15(m, d, x, ...) m(d, 14, x) BETTER_ENUMS_ID(BETTER_ENUMS_M14(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M16(m, d, x, ...) m(d, 15, x) BETTER_ENUMS_ID(BETTER_ENUMS_M15(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M17(m, d, x, ...) m(d, 16, x) BETTER_ENUMS_ID(BETTER_ENUMS_M16(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M18(m, d, x, ...) m(d, 17, x) BETTER_ENUMS_ID(BETTER_ENUMS_M17(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M19(m, d, x, ...) m(d, 18, x) BETTER_ENUMS_ID(BETTER_ENUMS_M18(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M20(m, d, x, ...) m(d, 19, x) BETTER_ENUMS_ID(BETTER_ENUMS_M19(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M21(m, d, x, ...) m(d, 20, x) BETTER_ENUMS_ID(BETTER_ENUMS_M20(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M22(m, d, x, ...) m(d, 21, x) BETTER_ENUMS_ID(BETTER_ENUMS_M21(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M23(m, d, x, ...) m(d, 22, x) BETTER_ENUMS_ID(BETTER_ENUMS_M22(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M24(m, d, x, ...) m(d, 23, x) BETTER_ENUMS_ID(BETTER_ENUMS_M23(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M25(m, d, x, ...) m(d, 24, x) BETTER_ENUMS_ID(BETTER_ENUMS_M24(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M26(m, d, x, ...) m(d, 25, x) BETTER_ENUMS_ID(BETTER_ENUMS_M25(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M27(m, d, x, ...) m(d, 26, x) BETTER_ENUMS_ID(BETTER_ENUMS_M26(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M28(m, d, x, ...) m(d, 27, x) BETTER_ENUMS_ID(BETTER_ENUMS_M27(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M29(m, d, x, ...) m(d, 28, x) BETTER_ENUMS_ID(BETTER_ENUMS_M28(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M30(m, d, x, ...) m(d, 29, x) BETTER_ENUMS_ID(BETTER_ENUMS_M29(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M31(m, d, x, ...) m(d, 30, x) BETTER_ENUMS_ID(BETTER_ENUMS_M30(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M32(m, d, x, ...) m(d, 31, x) BETTER_ENUMS_ID(BETTER_ENUMS_M31(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M33(m, d, x, ...) m(d, 32, x) BETTER_ENUMS_ID(BETTER_ENUMS_M32(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M34(m, d, x, ...) m(d, 33, x) BETTER_ENUMS_ID(BETTER_ENUMS_M33(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M35(m, d, x, ...) m(d, 34, x) BETTER_ENUMS_ID(BETTER_ENUMS_M34(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M36(m, d, x, ...) m(d, 35, x) BETTER_ENUMS_ID(BETTER_ENUMS_M35(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M37(m, d, x, ...) m(d, 36, x) BETTER_ENUMS_ID(BETTER_ENUMS_M36(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M38(m, d, x, ...) m(d, 37, x) BETTER_ENUMS_ID(BETTER_ENUMS_M37(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M39(m, d, x, ...) m(d, 38, x) BETTER_ENUMS_ID(BETTER_ENUMS_M38(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M40(m, d, x, ...) m(d, 39, x) BETTER_ENUMS_ID(BETTER_ENUMS_M39(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M41(m, d, x, ...) m(d, 40, x) BETTER_ENUMS_ID(BETTER_ENUMS_M40(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M42(m, d, x, ...) m(d, 41, x) BETTER_ENUMS_ID(BETTER_ENUMS_M41(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M43(m, d, x, ...) m(d, 42, x) BETTER_ENUMS_ID(BETTER_ENUMS_M42(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M44(m, d, x, ...) m(d, 43, x) BETTER_ENUMS_ID(BETTER_ENUMS_M43(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M45(m, d, x, ...) m(d, 44, x) BETTER_ENUMS_ID(BETTER_ENUMS_M44(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M46(m, d, x, ...) m(d, 45, x) BETTER_ENUMS_ID(BETTER_ENUMS_M45(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M47(m, d, x, ...) m(d, 46, x) BETTER_ENUMS_ID(BETTER_ENUMS_M46(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M48(m, d, x, ...) m(d, 47, x) BETTER_ENUMS_ID(BETTER_ENUMS_M47(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M49(m, d, x, ...) m(d, 48, x) BETTER_ENUMS_ID(BETTER_ENUMS_M48(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M50(m, d, x, ...) m(d, 49, x) BETTER_ENUMS_ID(BETTER_ENUMS_M49(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M51(m, d, x, ...) m(d, 50, x) BETTER_ENUMS_ID(BETTER_ENUMS_M50(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M52(m, d, x, ...) m(d, 51, x) BETTER_ENUMS_ID(BETTER_ENUMS_M51(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M53(m, d, x, ...) m(d, 52, x) BETTER_ENUMS_ID(BETTER_ENUMS_M52(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M54(m, d, x, ...) m(d, 53, x) BETTER_ENUMS_ID(BETTER_ENUMS_M53(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M55(m, d, x, ...) m(d, 54, x) BETTER_ENUMS_ID(BETTER_ENUMS_M54(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M56(m, d, x, ...) m(d, 55, x) BETTER_ENUMS_ID(BETTER_ENUMS_M55(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M57(m, d, x, ...) m(d, 56, x) BETTER_ENUMS_ID(BETTER_ENUMS_M56(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M58(m, d, x, ...) m(d, 57, x) BETTER_ENUMS_ID(BETTER_ENUMS_M57(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M59(m, d, x, ...) m(d, 58, x) BETTER_ENUMS_ID(BETTER_ENUMS_M58(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M60(m, d, x, ...) m(d, 59, x) BETTER_ENUMS_ID(BETTER_ENUMS_M59(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M61(m, d, x, ...) m(d, 60, x) BETTER_ENUMS_ID(BETTER_ENUMS_M60(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M62(m, d, x, ...) m(d, 61, x) BETTER_ENUMS_ID(BETTER_ENUMS_M61(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M63(m, d, x, ...) m(d, 62, x) BETTER_ENUMS_ID(BETTER_ENUMS_M62(m, d, __VA_ARGS__))
        #define BETTER_ENUMS_M64(m, d, x, ...) m(d, 63, x) BETTER_ENUMS_ID(BETTER_ENUMS_M63(m, d, __VA_ARGS__))

        #define BETTER_ENUMS_PP_COUNT_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, \
                                           _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32,  \
                                           _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47,  \
                                           _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62,  \
                                           _63, _64, count, ...)                                                       \
            count

        #define BETTER_ENUMS_PP_COUNT(...)                                                                             \
            BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT_IMPL(                                                                \
                    __VA_ARGS__, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44,   \
                    43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20,    \
                    19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))

        #define BETTER_ENUMS_ITERATE(X, f, l)                                                                          \
            X(f, l, 0)                                                                                                 \
            X(f, l, 1) X(f, l, 2) X(f, l, 3) X(f, l, 4) X(f, l, 5) X(f, l, 6) X(f, l, 7) X(f, l, 8) X(f, l, 9)         \
                    X(f, l, 10) X(f, l, 11) X(f, l, 12) X(f, l, 13) X(f, l, 14) X(f, l, 15) X(f, l, 16) X(f, l, 17)    \
                            X(f, l, 18) X(f, l, 19) X(f, l, 20) X(f, l, 21) X(f, l, 22) X(f, l, 23)

    #endif  // #ifdef BETTER_ENUMS_MACRO_FILE else case

namespace better_enums
{

    // Optional type.

    template<typename T>
    BETTER_ENUMS_CONSTEXPR_ inline T _default()
    {
        return static_cast<typename T::_enumerated>(0);
    }

    template<>
    BETTER_ENUMS_CONSTEXPR_ inline const char *_default<const char *>()
    {
        return BETTER_ENUMS_NULLPTR;
    }

    template<>
    BETTER_ENUMS_CONSTEXPR_ inline std::size_t _default<std::size_t>()
    {
        return 0;
    }

    template<typename T>
    struct optional
    {
        BETTER_ENUMS_CONSTEXPR_ optional() : _valid(false), _value(_default<T>()) { }

        BETTER_ENUMS_CONSTEXPR_ optional(T v) : _valid(true), _value(v) { }

        BETTER_ENUMS_CONSTEXPR_ const T &operator*() const { return _value; }
        BETTER_ENUMS_CONSTEXPR_ const T *operator->() const { return &_value; }

        BETTER_ENUMS_CONSTEXPR_ operator bool() const { return _valid; }

        BETTER_ENUMS_CONSTEXPR_ const T &value() const { return _value; }

    private:
        bool _valid;
        T _value;
    };

    template<typename CastTo, typename Element>
    BETTER_ENUMS_CONSTEXPR_ static optional<CastTo> _map_index(const Element *array, optional<std::size_t> index)
    {
        return index ? static_cast<CastTo>(array[*index]) : optional<CastTo>();
    }

    #ifdef BETTER_ENUMS_VC2008_WORKAROUNDS

        #define BETTER_ENUMS_OR_THROW                                                                                  \
            if (!maybe)                                                                                                \
                throw std::runtime_error(message);                                                                     \
                                                                                                                       \
            return *maybe;

    #else

        #define BETTER_ENUMS_OR_THROW return maybe ? *maybe : throw std::runtime_error(message);

    #endif

    BETTER_ENUMS_IF_EXCEPTIONS(template<typename T>
                               BETTER_ENUMS_CONSTEXPR_ static T _or_throw(optional<T> maybe,
                                                                          const char *message){ BETTER_ENUMS_OR_THROW })

    template<typename T>
    BETTER_ENUMS_CONSTEXPR_ static T *_or_null(optional<T *> maybe)
    {
        return maybe ? *maybe : BETTER_ENUMS_NULLPTR;
    }

    template<typename T>
    BETTER_ENUMS_CONSTEXPR_ static T _or_zero(optional<T> maybe)
    {
        return maybe ? *maybe : T::_from_integral_unchecked(0);
    }

    // Functional sequencing. This is essentially a comma operator wrapped in a
    // constexpr function. g++ 4.7 doesn't "accept" integral constants in the second
    // position for the comma operator, and emits an external symbol, which then
    // causes a linking error.

    template<typename T, typename U>
    BETTER_ENUMS_CONSTEXPR_ U continue_with(T, U value)
    {
        return value;
    }

    // Values array declaration helper.

    //! Get intrinsic value of an (Enum::value) by taking advantage of
    // C-conversion's parentheses priority
    template<typename EnumType>
    struct _eat_assign
    {
        explicit BETTER_ENUMS_CONSTEXPR_ _eat_assign(EnumType value) : _value(value) { }

        template<typename Any>
        BETTER_ENUMS_CONSTEXPR_ const _eat_assign &operator=(Any) const
        {
            return *this;
        }

        BETTER_ENUMS_CONSTEXPR_ operator EnumType() const { return _value; }

    private:
        EnumType _value;
    };

    // Iterables.

    template<typename Element>
    struct _iterable
    {
        typedef const Element *iterator;

        BETTER_ENUMS_CONSTEXPR_ iterator begin() const { return iterator(_array); }
        BETTER_ENUMS_CONSTEXPR_ iterator end() const { return iterator(_array + _size); }
        BETTER_ENUMS_CONSTEXPR_ std::size_t size() const { return _size; }
        BETTER_ENUMS_CONSTEXPR_ const Element &operator[](std::size_t index) const { return _array[index]; }

        BETTER_ENUMS_CONSTEXPR_ _iterable(const Element *array, std::size_t s) : _array(array), _size(s) { }

    private:
        const Element *const _array;
        const std::size_t _size;
    };

    // String routines.

    BETTER_ENUMS_CONSTEXPR_ static const char *_name_enders = "= \t\n";

    BETTER_ENUMS_CONSTEXPR_ inline bool _ends_name(char c, std::size_t index = 0)
    {
        return c == _name_enders[index] ? true : _name_enders[index] == '\0' ? false : _ends_name(c, index + 1);
    }

    BETTER_ENUMS_CONSTEXPR_ inline bool _has_initializer(const char *s, std::size_t index = 0)
    {
        return s[index] == '\0' ? false : s[index] == '=' ? true : _has_initializer(s, index + 1);
    }

    BETTER_ENUMS_CONSTEXPR_ inline std::size_t _constant_length(const char *s, std::size_t index = 0)
    {
        return _ends_name(s[index]) ? index : _constant_length(s, index + 1);
    }

    BETTER_ENUMS_CONSTEXPR_ inline char _select(const char *from, std::size_t from_length, std::size_t index)
    {
        return index >= from_length ? '\0' : from[index];
    }

    BETTER_ENUMS_CONSTEXPR_ inline char _to_lower_ascii(char c)
    {
        return c >= 0x41 && c <= 0x5A ? static_cast<char>(c + 0x20) : c;
    }

    BETTER_ENUMS_CONSTEXPR_ inline bool _names_match(const char *stringizedName, const char *referenceName,
                                                     std::size_t index = 0)
    {
        return _ends_name(stringizedName[index])
                ? referenceName[index] == '\0'
                : referenceName[index] == '\0' ? false
                                               : stringizedName[index] != referenceName[index]
                                ? false
                                : _names_match(stringizedName, referenceName, index + 1);
    }

    BETTER_ENUMS_CONSTEXPR_ inline bool _names_match_nocase(const char *stringizedName, const char *referenceName,
                                                            std::size_t index = 0)
    {
        return _ends_name(stringizedName[index]) ? referenceName[index] == '\0'
                                                 : referenceName[index] == '\0'
                        ? false
                        : _to_lower_ascii(stringizedName[index]) != _to_lower_ascii(referenceName[index])
                                ? false
                                : _names_match_nocase(stringizedName, referenceName, index + 1);
    }

    inline void _trim_names(const char *const *raw_names, const char **trimmed_names, char *storage, std::size_t count)
    {
        std::size_t offset = 0;

        for (std::size_t index = 0; index < count; ++index) {
            trimmed_names[index] = storage + offset;

            std::size_t trimmed_length       = std::strcspn(raw_names[index], _name_enders);
            storage[offset + trimmed_length] = '\0';

            std::size_t raw_length = std::strlen(raw_names[index]);
            offset += raw_length + 1;
        }
    }

    // Eager initialization.
    template<typename Enum>
    struct _initialize_at_program_start
    {
        _initialize_at_program_start() { Enum::initialize(); }
    };

}  // namespace better_enums

// Array generation macros.

    #define BETTER_ENUMS_EAT_ASSIGN_SINGLE(EnumType, index, expression)                                                \
        (EnumType)((::better_enums::_eat_assign<EnumType>)EnumType::expression),

    #define BETTER_ENUMS_EAT_ASSIGN(EnumType, ...)                                                                     \
        BETTER_ENUMS_ID(BETTER_ENUMS_PP_MAP(BETTER_ENUMS_EAT_ASSIGN_SINGLE, EnumType, __VA_ARGS__))

    #ifdef BETTER_ENUMS_HAVE_CONSTEXPR

        #define BETTER_ENUMS_SELECT_SINGLE_CHARACTER(from, from_length, index)                                         \
            ::better_enums::_select(from, from_length, index),

        #define BETTER_ENUMS_SELECT_CHARACTERS(from, from_length)                                                      \
            BETTER_ENUMS_ITERATE(BETTER_ENUMS_SELECT_SINGLE_CHARACTER, from, from_length)

        #define BETTER_ENUMS_TRIM_SINGLE_STRING(ignored, index, expression)                                            \
            constexpr std::size_t _length_##index   = ::better_enums::_constant_length(#expression);                   \
            constexpr const char _trimmed_##index[] = { BETTER_ENUMS_SELECT_CHARACTERS(#expression,                    \
                                                                                       _length_##index) };             \
            constexpr const char *_final_##index =                                                                     \
                    ::better_enums::_has_initializer(#expression) ? _trimmed_##index : #expression;

        #define BETTER_ENUMS_TRIM_STRINGS(...)                                                                         \
            BETTER_ENUMS_ID(BETTER_ENUMS_PP_MAP(BETTER_ENUMS_TRIM_SINGLE_STRING, ignored, __VA_ARGS__))

        #define BETTER_ENUMS_REFER_TO_SINGLE_STRING(ignored, index, expression) _final_##index,

        #define BETTER_ENUMS_REFER_TO_STRINGS(...)                                                                     \
            BETTER_ENUMS_ID(BETTER_ENUMS_PP_MAP(BETTER_ENUMS_REFER_TO_SINGLE_STRING, ignored, __VA_ARGS__))

    #endif  // #ifdef BETTER_ENUMS_HAVE_CONSTEXPR

    #define BETTER_ENUMS_STRINGIZE_SINGLE(ignored, index, expression) #expression,

    #define BETTER_ENUMS_STRINGIZE(...)                                                                                \
        BETTER_ENUMS_ID(BETTER_ENUMS_PP_MAP(BETTER_ENUMS_STRINGIZE_SINGLE, ignored, __VA_ARGS__))

    #define BETTER_ENUMS_RESERVE_STORAGE_SINGLE(ignored, index, expression) #expression ","

    #define BETTER_ENUMS_RESERVE_STORAGE(...)                                                                          \
        BETTER_ENUMS_ID(BETTER_ENUMS_PP_MAP(BETTER_ENUMS_RESERVE_STORAGE_SINGLE, ignored, __VA_ARGS__))

// The enums proper.

    #define BETTER_ENUMS_NS(EnumType) better_enums_data_##EnumType

    #ifdef BETTER_ENUMS_VC2008_WORKAROUNDS

        #define BETTER_ENUMS_COPY_CONSTRUCTOR(Enum)                                                                    \
            BETTER_ENUMS_CONSTEXPR_ Enum(const Enum &other) : _value(other._value) { }

    #else

        #define BETTER_ENUMS_COPY_CONSTRUCTOR(Enum)

    #endif

    #ifndef BETTER_ENUMS_CLASS_ATTRIBUTE
        #define BETTER_ENUMS_CLASS_ATTRIBUTE
    #endif

    #define BETTER_ENUMS_TYPE(SetUnderlyingType, SwitchType, GenerateSwitchType, GenerateStrings, ToStringConstexpr,   \
                              DeclareInitialize, DefineInitialize, CallInitialize, Enum, Underlying, ...)              \
                                                                                                                       \
        namespace better_enums_data_##Enum { BETTER_ENUMS_ID(GenerateSwitchType(Underlying, __VA_ARGS__)) }            \
                                                                                                                       \
        class BETTER_ENUMS_CLASS_ATTRIBUTE Enum                                                                        \
        {                                                                                                              \
        private:                                                                                                       \
            typedef ::better_enums::optional<Enum> _optional;                                                          \
            typedef ::better_enums::optional<std::size_t> _optional_index;                                             \
                                                                                                                       \
        public:                                                                                                        \
            typedef Underlying _integral;                                                                              \
                                                                                                                       \
            enum _enumerated SetUnderlyingType(Underlying){ __VA_ARGS__ };                                             \
                                                                                                                       \
            BETTER_ENUMS_CONSTEXPR_ Enum(_enumerated value) : _value(value) { }                                        \
                                                                                                                       \
            BETTER_ENUMS_COPY_CONSTRUCTOR(Enum)                                                                        \
                                                                                                                       \
            BETTER_ENUMS_CONSTEXPR_ operator SwitchType(Enum)() const { return SwitchType(Enum)(_value); }             \
                                                                                                                       \
            BETTER_ENUMS_CONSTEXPR_ _integral _to_integral() const;                                                    \
            BETTER_ENUMS_IF_EXCEPTIONS(BETTER_ENUMS_CONSTEXPR_ static Enum _from_integral(_integral value);)           \
            BETTER_ENUMS_CONSTEXPR_ static Enum _from_integral_unchecked(_integral value);                             \
            BETTER_ENUMS_CONSTEXPR_ static _optional _from_integral_nothrow(_integral value);                          \
                                                                                                                       \
            BETTER_ENUMS_CONSTEXPR_ std::size_t _to_index() const;                                                     \
            BETTER_ENUMS_IF_EXCEPTIONS(BETTER_ENUMS_CONSTEXPR_ static Enum _from_index(std::size_t index);)            \
            BETTER_ENUMS_CONSTEXPR_ static Enum _from_index_unchecked(std::size_t index);                              \
            BETTER_ENUMS_CONSTEXPR_ static _optional _from_index_nothrow(std::size_t index);                           \
                                                                                                                       \
            ToStringConstexpr const char *_to_string() const;                                                          \
            BETTER_ENUMS_IF_EXCEPTIONS(BETTER_ENUMS_CONSTEXPR_ static Enum _from_string(const char *name);)            \
            BETTER_ENUMS_CONSTEXPR_ static _optional _from_string_nothrow(const char *name);                           \
                                                                                                                       \
            BETTER_ENUMS_IF_EXCEPTIONS(BETTER_ENUMS_CONSTEXPR_ static Enum _from_string_nocase(const char *name);)     \
            BETTER_ENUMS_CONSTEXPR_ static _optional _from_string_nocase_nothrow(const char *name);                    \
                                                                                                                       \
            BETTER_ENUMS_CONSTEXPR_ static bool _is_valid(_integral value);                                            \
            BETTER_ENUMS_CONSTEXPR_ static bool _is_valid(const char *name);                                           \
            BETTER_ENUMS_CONSTEXPR_ static bool _is_valid_nocase(const char *name);                                    \
                                                                                                                       \
            typedef ::better_enums::_iterable<Enum> _value_iterable;                                                   \
            typedef ::better_enums::_iterable<const char *> _name_iterable;                                            \
                                                                                                                       \
            typedef _value_iterable::iterator _value_iterator;                                                         \
            typedef _name_iterable::iterator _name_iterator;                                                           \
                                                                                                                       \
            BETTER_ENUMS_CONSTEXPR_ static const std::size_t _size_constant =                                          \
                    BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT(__VA_ARGS__));                                               \
            BETTER_ENUMS_CONSTEXPR_ static std::size_t _size() { return _size_constant; }                              \
                                                                                                                       \
            BETTER_ENUMS_CONSTEXPR_ static const char *_name();                                                        \
            BETTER_ENUMS_CONSTEXPR_ static _value_iterable _values();                                                  \
            ToStringConstexpr static _name_iterable _names();                                                          \
                                                                                                                       \
            _integral _value;                                                                                          \
                                                                                                                       \
            BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum)                                                                     \
                                                                                                                       \
        private:                                                                                                       \
            explicit BETTER_ENUMS_CONSTEXPR_ Enum(const _integral &value) : _value(value) { }                          \
                                                                                                                       \
            DeclareInitialize                                                                                          \
                                                                                                                       \
                    BETTER_ENUMS_CONSTEXPR_ static _optional_index                                                     \
                    _from_value_loop(_integral value, std::size_t index = 0);                                          \
            BETTER_ENUMS_CONSTEXPR_ static _optional_index _from_string_loop(const char *name, std::size_t index = 0); \
            BETTER_ENUMS_CONSTEXPR_ static _optional_index _from_string_nocase_loop(const char *name,                  \
                                                                                    std::size_t index = 0);            \
                                                                                                                       \
            friend struct ::better_enums::_initialize_at_program_start<Enum>;                                          \
        };                                                                                                             \
                                                                                                                       \
        namespace better_enums_data_##Enum                                                                             \
        {                                                                                                              \
                                                                                                                       \
            static ::better_enums::_initialize_at_program_start<Enum> _force_initialization;                           \
                                                                                                                       \
            enum _putNamesInThisScopeAlso                                                                              \
            {                                                                                                          \
                __VA_ARGS__                                                                                            \
            };                                                                                                         \
                                                                                                                       \
            BETTER_ENUMS_IGNORE_OLD_CAST_HEADER                                                                        \
            BETTER_ENUMS_IGNORE_OLD_CAST_BEGIN                                                                         \
            BETTER_ENUMS_CONSTEXPR_ const Enum _value_array[] = { BETTER_ENUMS_ID(                                     \
                    BETTER_ENUMS_EAT_ASSIGN(Enum, __VA_ARGS__)) };                                                     \
            BETTER_ENUMS_IGNORE_OLD_CAST_END                                                                           \
                                                                                                                       \
            BETTER_ENUMS_ID(GenerateStrings(Enum, __VA_ARGS__))                                                        \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_IGNORE_ATTRIBUTES_HEADER                                                                          \
        BETTER_ENUMS_IGNORE_ATTRIBUTES_BEGIN                                                                           \
        BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ inline const Enum operator+(Enum::_enumerated enumerated)          \
        {                                                                                                              \
            return static_cast<Enum>(enumerated);                                                                      \
        }                                                                                                              \
        BETTER_ENUMS_IGNORE_ATTRIBUTES_END                                                                             \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional_index Enum::_from_value_loop(Enum::_integral value,             \
                                                                                    std::size_t index)                 \
        {                                                                                                              \
            return index == _size()                                                                                    \
                    ? _optional_index()                                                                                \
                    : BETTER_ENUMS_NS(Enum)::_value_array[index]._value == value ? _optional_index(index)              \
                                                                                 : _from_value_loop(value, index + 1); \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional_index Enum::_from_string_loop(const char *name,                 \
                                                                                     std::size_t index)                \
        {                                                                                                              \
            return index == _size() ? _optional_index()                                                                \
                                    : ::better_enums::_names_match(BETTER_ENUMS_NS(Enum)::_raw_names()[index], name)   \
                            ? _optional_index(index)                                                                   \
                            : _from_string_loop(name, index + 1);                                                      \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional_index Enum::_from_string_nocase_loop(const char *name,          \
                                                                                            std::size_t index)         \
        {                                                                                                              \
            return index == _size()                                                                                    \
                    ? _optional_index()                                                                                \
                    : ::better_enums::_names_match_nocase(BETTER_ENUMS_NS(Enum)::_raw_names()[index], name)            \
                            ? _optional_index(index)                                                                   \
                            : _from_string_nocase_loop(name, index + 1);                                               \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_integral Enum::_to_integral() const { return _integral(_value); }        \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline std::size_t Enum::_to_index() const { return *_from_value_loop(_value); }       \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_index_unchecked(std::size_t index)                             \
        {                                                                                                              \
            return ::better_enums::_or_zero(_from_index_nothrow(index));                                               \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional Enum::_from_index_nothrow(std::size_t index)                    \
        {                                                                                                              \
            return index >= _size() ? _optional() : _optional(BETTER_ENUMS_NS(Enum)::_value_array[index]);             \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_IF_EXCEPTIONS(BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_index(std::size_t index) {          \
            return ::better_enums::_or_throw(_from_index_nothrow(index), #Enum "::_from_index: invalid argument");     \
        })                                                                                                             \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_integral_unchecked(_integral value)                            \
        {                                                                                                              \
            return static_cast<_enumerated>(value);                                                                    \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional Enum::_from_integral_nothrow(_integral value)                   \
        {                                                                                                              \
            return ::better_enums::_map_index<Enum>(BETTER_ENUMS_NS(Enum)::_value_array, _from_value_loop(value));     \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_IF_EXCEPTIONS(BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_integral(_integral value) {         \
            return ::better_enums::_or_throw(_from_integral_nothrow(value),                                            \
                                             #Enum "::_from_integral: invalid argument");                              \
        })                                                                                                             \
                                                                                                                       \
        ToStringConstexpr inline const char *Enum::_to_string() const                                                  \
        {                                                                                                              \
            return ::better_enums::_or_null(::better_enums::_map_index<const char *>(                                  \
                    BETTER_ENUMS_NS(Enum)::_name_array(), _from_value_loop(CallInitialize(_value))));                  \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional Enum::_from_string_nothrow(const char *name)                    \
        {                                                                                                              \
            return ::better_enums::_map_index<Enum>(BETTER_ENUMS_NS(Enum)::_value_array, _from_string_loop(name));     \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_IF_EXCEPTIONS(BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_string(const char *name) {          \
            return ::better_enums::_or_throw(_from_string_nothrow(name), #Enum "::_from_string: invalid argument");    \
        })                                                                                                             \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional Enum::_from_string_nocase_nothrow(const char *name)             \
        {                                                                                                              \
            return ::better_enums::_map_index<Enum>(BETTER_ENUMS_NS(Enum)::_value_array,                               \
                                                    _from_string_nocase_loop(name));                                   \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_IF_EXCEPTIONS(BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_string_nocase(const char *name) {   \
            return ::better_enums::_or_throw(_from_string_nocase_nothrow(name),                                        \
                                             #Enum "::_from_string_nocase: invalid argument");                         \
        })                                                                                                             \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline bool Enum::_is_valid(_integral value) { return _from_value_loop(value); }       \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline bool Enum::_is_valid(const char *name) { return _from_string_loop(name); }      \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline bool Enum::_is_valid_nocase(const char *name)                                   \
        {                                                                                                              \
            return _from_string_nocase_loop(name);                                                                     \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline const char *Enum::_name() { return #Enum; }                                     \
                                                                                                                       \
        BETTER_ENUMS_CONSTEXPR_ inline Enum::_value_iterable Enum::_values()                                           \
        {                                                                                                              \
            return _value_iterable(BETTER_ENUMS_NS(Enum)::_value_array, _size());                                      \
        }                                                                                                              \
                                                                                                                       \
        ToStringConstexpr inline Enum::_name_iterable Enum::_names()                                                   \
        {                                                                                                              \
            return _name_iterable(BETTER_ENUMS_NS(Enum)::_name_array(), CallInitialize(_size()));                      \
        }                                                                                                              \
                                                                                                                       \
        DefineInitialize(Enum)                                                                                         \
                                                                                                                       \
                BETTER_ENUMS_IGNORE_ATTRIBUTES_HEADER BETTER_ENUMS_IGNORE_ATTRIBUTES_BEGIN BETTER_ENUMS_UNUSED         \
                        BETTER_ENUMS_CONSTEXPR_ inline bool                                                            \
                        operator==(const Enum &a, const Enum &b)                                                       \
        {                                                                                                              \
            return a._to_integral() == b._to_integral();                                                               \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ inline bool operator!=(const Enum &a, const Enum &b)               \
        {                                                                                                              \
            return a._to_integral() != b._to_integral();                                                               \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ inline bool operator<(const Enum &a, const Enum &b)                \
        {                                                                                                              \
            return a._to_integral() < b._to_integral();                                                                \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ inline bool operator<=(const Enum &a, const Enum &b)               \
        {                                                                                                              \
            return a._to_integral() <= b._to_integral();                                                               \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ inline bool operator>(const Enum &a, const Enum &b)                \
        {                                                                                                              \
            return a._to_integral() > b._to_integral();                                                                \
        }                                                                                                              \
                                                                                                                       \
        BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ inline bool operator>=(const Enum &a, const Enum &b)               \
        {                                                                                                              \
            return a._to_integral() >= b._to_integral();                                                               \
        }                                                                                                              \
        BETTER_ENUMS_IGNORE_ATTRIBUTES_END                                                                             \
                                                                                                                       \
        template<typename Char, typename Traits>                                                                       \
        std::basic_ostream<Char, Traits> &operator<<(std::basic_ostream<Char, Traits> &stream, const Enum &value)      \
        {                                                                                                              \
            return stream << value._to_string();                                                                       \
        }                                                                                                              \
                                                                                                                       \
        template<typename Char, typename Traits>                                                                       \
        std::basic_istream<Char, Traits> &operator>>(std::basic_istream<Char, Traits> &stream, Enum &value)            \
        {                                                                                                              \
            std::basic_string<Char, Traits> buffer;                                                                    \
                                                                                                                       \
            stream >> buffer;                                                                                          \
            ::better_enums::optional<Enum> converted = Enum::_from_string_nothrow(buffer.c_str());                     \
                                                                                                                       \
            if (converted)                                                                                             \
                value = *converted;                                                                                    \
            else                                                                                                       \
                stream.setstate(std::basic_istream<Char, Traits>::failbit);                                            \
                                                                                                                       \
            return stream;                                                                                             \
        }

    // Enum feature options.

    // C++98, C++11
    #define BETTER_ENUMS_CXX98_UNDERLYING_TYPE(Underlying)

    // C++11
    #define BETTER_ENUMS_CXX11_UNDERLYING_TYPE(Underlying)                                                             \
    : Underlying

    #if defined(_MSC_VER) && _MSC_VER >= 1700
        // VS 2012 and above fully support strongly typed enums and will warn about
        // incorrect usage.
        #define BETTER_ENUMS_LEGACY_UNDERLYING_TYPE(Underlying) BETTER_ENUMS_CXX11_UNDERLYING_TYPE(Underlying)
    #else
        #define BETTER_ENUMS_LEGACY_UNDERLYING_TYPE(Underlying) BETTER_ENUMS_CXX98_UNDERLYING_TYPE(Underlying)
    #endif

    // C++98, C++11
    #define BETTER_ENUMS_REGULAR_ENUM_SWITCH_TYPE(Type) _enumerated

    // C++11
    #define BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE(Type) BETTER_ENUMS_NS(Type)::_enumClassForSwitchStatements

    // C++98, C++11
    #define BETTER_ENUMS_REGULAR_ENUM_SWITCH_TYPE_GENERATE(Underlying, ...)

    // C++11
    #define BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE_GENERATE(Underlying, ...)                                              \
        enum class _enumClassForSwitchStatements : Underlying                                                          \
        {                                                                                                              \
            __VA_ARGS__                                                                                                \
        };

    // C++98
    #define BETTER_ENUMS_CXX98_TRIM_STRINGS_ARRAYS(Enum, ...)                                                          \
        inline const char **_raw_names()                                                                               \
        {                                                                                                              \
            static const char *value[] = { BETTER_ENUMS_ID(BETTER_ENUMS_STRINGIZE(__VA_ARGS__)) };                     \
            return value;                                                                                              \
        }                                                                                                              \
                                                                                                                       \
        inline char *_name_storage()                                                                                   \
        {                                                                                                              \
            static char storage[] = BETTER_ENUMS_ID(BETTER_ENUMS_RESERVE_STORAGE(__VA_ARGS__));                        \
            return storage;                                                                                            \
        }                                                                                                              \
                                                                                                                       \
        inline const char **_name_array()                                                                              \
        {                                                                                                              \
            static const char *value[Enum::_size_constant];                                                            \
            return value;                                                                                              \
        }                                                                                                              \
                                                                                                                       \
        inline bool &_initialized()                                                                                    \
        {                                                                                                              \
            static bool value = false;                                                                                 \
            return value;                                                                                              \
        }

    // C++11 fast version
    #define BETTER_ENUMS_CXX11_PARTIAL_CONSTEXPR_TRIM_STRINGS_ARRAYS(Enum, ...)                                        \
        constexpr const char *_the_raw_names[] = { BETTER_ENUMS_ID(BETTER_ENUMS_STRINGIZE(__VA_ARGS__)) };             \
                                                                                                                       \
        constexpr const char *const *_raw_names() { return _the_raw_names; }                                           \
                                                                                                                       \
        inline char *_name_storage()                                                                                   \
        {                                                                                                              \
            static char storage[] = BETTER_ENUMS_ID(BETTER_ENUMS_RESERVE_STORAGE(__VA_ARGS__));                        \
            return storage;                                                                                            \
        }                                                                                                              \
                                                                                                                       \
        inline const char **_name_array()                                                                              \
        {                                                                                                              \
            static const char *value[Enum::_size_constant];                                                            \
            return value;                                                                                              \
        }                                                                                                              \
                                                                                                                       \
        inline bool &_initialized()                                                                                    \
        {                                                                                                              \
            static bool value = false;                                                                                 \
            return value;                                                                                              \
        }

    // C++11 slow all-constexpr version
    #define BETTER_ENUMS_CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS(Enum, ...)                                           \
        BETTER_ENUMS_ID(BETTER_ENUMS_TRIM_STRINGS(__VA_ARGS__))                                                        \
                                                                                                                       \
        constexpr const char *const _the_name_array[] = { BETTER_ENUMS_ID(                                             \
                BETTER_ENUMS_REFER_TO_STRINGS(__VA_ARGS__)) };                                                         \
                                                                                                                       \
        constexpr const char *const *_name_array() { return _the_name_array; }                                         \
                                                                                                                       \
        constexpr const char *const *_raw_names() { return _the_name_array; }

    // C++98, C++11 fast version
    #define BETTER_ENUMS_NO_CONSTEXPR_TO_STRING_KEYWORD

    // C++11 slow all-constexpr version
    #define BETTER_ENUMS_CONSTEXPR_TO_STRING_KEYWORD constexpr

    // C++98, C++11 fast version
    #define BETTER_ENUMS_DO_DECLARE_INITIALIZE static int initialize();

    // C++11 slow all-constexpr version
    #define BETTER_ENUMS_DECLARE_EMPTY_INITIALIZE                                                                      \
        static int initialize() { return 0; }

    // C++98, C++11 fast version
    #define BETTER_ENUMS_DO_DEFINE_INITIALIZE(Enum)                                                                    \
        inline int Enum::initialize()                                                                                  \
        {                                                                                                              \
            if (BETTER_ENUMS_NS(Enum)::_initialized())                                                                 \
                return 0;                                                                                              \
                                                                                                                       \
            ::better_enums::_trim_names(BETTER_ENUMS_NS(Enum)::_raw_names(), BETTER_ENUMS_NS(Enum)::_name_array(),     \
                                        BETTER_ENUMS_NS(Enum)::_name_storage(), _size());                              \
                                                                                                                       \
            BETTER_ENUMS_NS(Enum)::_initialized() = true;                                                              \
                                                                                                                       \
            return 0;                                                                                                  \
        }

    // C++11 slow all-constexpr version
    #define BETTER_ENUMS_DO_NOT_DEFINE_INITIALIZE(Enum)

    // C++98, C++11 fast version
    #define BETTER_ENUMS_DO_CALL_INITIALIZE(value) ::better_enums::continue_with(initialize(), value)

    // C++11 slow all-constexpr version
    #define BETTER_ENUMS_DO_NOT_CALL_INITIALIZE(value) value

// User feature selection.

    #ifdef BETTER_ENUMS_STRICT_CONVERSION
        #define BETTER_ENUMS_DEFAULT_SWITCH_TYPE          BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE
        #define BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE_GENERATE
    #else
        #define BETTER_ENUMS_DEFAULT_SWITCH_TYPE          BETTER_ENUMS_REGULAR_ENUM_SWITCH_TYPE
        #define BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE BETTER_ENUMS_REGULAR_ENUM_SWITCH_TYPE_GENERATE
    #endif

    #ifndef BETTER_ENUMS_DEFAULT_CONSTRUCTOR
        #define BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum)                                                                 \
        private:                                                                                                       \
            Enum() : _value(0) { }
    #endif

    #ifdef BETTER_ENUMS_HAVE_CONSTEXPR

        #ifdef BETTER_ENUMS_CONSTEXPR_TO_STRING
            #define BETTER_ENUMS_DEFAULT_TRIM_STRINGS_ARRAYS BETTER_ENUMS_CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS
            #define BETTER_ENUMS_DEFAULT_TO_STRING_KEYWORD   BETTER_ENUMS_CONSTEXPR_TO_STRING_KEYWORD
            #define BETTER_ENUMS_DEFAULT_DECLARE_INITIALIZE  BETTER_ENUMS_DECLARE_EMPTY_INITIALIZE
            #define BETTER_ENUMS_DEFAULT_DEFINE_INITIALIZE   BETTER_ENUMS_DO_NOT_DEFINE_INITIALIZE
            #define BETTER_ENUMS_DEFAULT_CALL_INITIALIZE     BETTER_ENUMS_DO_NOT_CALL_INITIALIZE
        #else
            #define BETTER_ENUMS_DEFAULT_TRIM_STRINGS_ARRAYS BETTER_ENUMS_CXX11_PARTIAL_CONSTEXPR_TRIM_STRINGS_ARRAYS
            #define BETTER_ENUMS_DEFAULT_TO_STRING_KEYWORD   BETTER_ENUMS_NO_CONSTEXPR_TO_STRING_KEYWORD
            #define BETTER_ENUMS_DEFAULT_DECLARE_INITIALIZE  BETTER_ENUMS_DO_DECLARE_INITIALIZE
            #define BETTER_ENUMS_DEFAULT_DEFINE_INITIALIZE   BETTER_ENUMS_DO_DEFINE_INITIALIZE
            #define BETTER_ENUMS_DEFAULT_CALL_INITIALIZE     BETTER_ENUMS_DO_CALL_INITIALIZE
        #endif

    // Top-level macros.

        #define BETTER_ENUM(Enum, Underlying, ...)                                                                     \
            BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(                                                                         \
                    BETTER_ENUMS_CXX11_UNDERLYING_TYPE, BETTER_ENUMS_DEFAULT_SWITCH_TYPE,                              \
                    BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE, BETTER_ENUMS_DEFAULT_TRIM_STRINGS_ARRAYS,               \
                    BETTER_ENUMS_DEFAULT_TO_STRING_KEYWORD, BETTER_ENUMS_DEFAULT_DECLARE_INITIALIZE,                   \
                    BETTER_ENUMS_DEFAULT_DEFINE_INITIALIZE, BETTER_ENUMS_DEFAULT_CALL_INITIALIZE, Enum, Underlying,    \
                    __VA_ARGS__))

        #define SLOW_ENUM(Enum, Underlying, ...)                                                                       \
            BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(                                                                         \
                    BETTER_ENUMS_CXX11_UNDERLYING_TYPE, BETTER_ENUMS_DEFAULT_SWITCH_TYPE,                              \
                    BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE, BETTER_ENUMS_CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS,  \
                    BETTER_ENUMS_CONSTEXPR_TO_STRING_KEYWORD, BETTER_ENUMS_DECLARE_EMPTY_INITIALIZE,                   \
                    BETTER_ENUMS_DO_NOT_DEFINE_INITIALIZE, BETTER_ENUMS_DO_NOT_CALL_INITIALIZE, Enum, Underlying,      \
                    __VA_ARGS__))

    #else

        #define BETTER_ENUM(Enum, Underlying, ...)                                                                     \
            BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(BETTER_ENUMS_LEGACY_UNDERLYING_TYPE, BETTER_ENUMS_DEFAULT_SWITCH_TYPE,   \
                                              BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE,                               \
                                              BETTER_ENUMS_CXX98_TRIM_STRINGS_ARRAYS,                                  \
                                              BETTER_ENUMS_NO_CONSTEXPR_TO_STRING_KEYWORD,                             \
                                              BETTER_ENUMS_DO_DECLARE_INITIALIZE, BETTER_ENUMS_DO_DEFINE_INITIALIZE,   \
                                              BETTER_ENUMS_DO_CALL_INITIALIZE, Enum, Underlying, __VA_ARGS__))

    #endif

namespace better_enums
{

    // Maps.

    template<typename T>
    struct map_compare
    {
        BETTER_ENUMS_CONSTEXPR_ static bool less(const T &a, const T &b) { return a < b; }
    };

    template<>
    struct map_compare<const char *>
    {
        BETTER_ENUMS_CONSTEXPR_ static bool less(const char *a, const char *b) { return less_loop(a, b); }

    private:
        BETTER_ENUMS_CONSTEXPR_ static bool less_loop(const char *a, const char *b, size_t index = 0)
        {
            return a[index] != b[index] ? a[index] < b[index] : a[index] == '\0' ? false : less_loop(a, b, index + 1);
        }
    };

    template<>
    struct map_compare<const wchar_t *>
    {
        BETTER_ENUMS_CONSTEXPR_ static bool less(const wchar_t *a, const wchar_t *b) { return less_loop(a, b); }

    private:
        BETTER_ENUMS_CONSTEXPR_ static bool less_loop(const wchar_t *a, const wchar_t *b, size_t index = 0)
        {
            return a[index] != b[index] ? a[index] < b[index] : a[index] == L'\0' ? false : less_loop(a, b, index + 1);
        }
    };

    template<typename Enum, typename T, typename Compare = map_compare<T>>
    struct map
    {
        typedef T (*function)(Enum);

        BETTER_ENUMS_CONSTEXPR_ explicit map(function f) : _f(f) { }

        BETTER_ENUMS_CONSTEXPR_ T from_enum(Enum value) const { return _f(value); }
        BETTER_ENUMS_CONSTEXPR_ T operator[](Enum value) const { return _f(value); }

        BETTER_ENUMS_CONSTEXPR_ Enum to_enum(T value) const
        {
            return _or_throw(to_enum_nothrow(value), "map::to_enum: invalid argument");
        }

        BETTER_ENUMS_CONSTEXPR_ optional<Enum> to_enum_nothrow(T value, size_t index = 0) const
        {
            return index >= Enum::_size() ? optional<Enum>()
                                          : Compare::less(_f(Enum::_values()[index]), value)
                                    || Compare::less(value, _f(Enum::_values()[index]))
                            ? to_enum_nothrow(value, index + 1)
                            : Enum::_values()[index];
        }

    private:
        const function _f;
    };

    template<typename Enum, typename T>
    BETTER_ENUMS_CONSTEXPR_ map<Enum, T> make_map(T (*f)(Enum))
    {
        return map<Enum, T>(f);
    }

}  // namespace better_enums

    #define BETTER_ENUMS_DECLARE_STD_HASH(type)                                                                        \
        namespace std                                                                                                  \
        {                                                                                                              \
            template<>                                                                                                 \
            struct hash<type>                                                                                          \
            {                                                                                                          \
                size_t operator()(const type &x) const { return std::hash<size_t>()(x._to_integral()); }               \
            };                                                                                                         \
        }

#endif  // #ifndef BETTER_ENUMS_ENUM_H
